﻿//-----------------------------------------------------------------------
// <copyright file="ProblemBase.cs" company="Numbrella foundation">
// Copyright (c) Numbrella foundation. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace Numbrella.Core
{
	using System;
	using System.Collections.Generic;
	using System.Globalization;
	using System.IO;
	using Numbrella.Interaction;

	/// <summary>
	/// Represents the very base class for all the implementations of <see cref="IProblem"/> interface.
	/// </summary>
	public abstract class ProblemBase : IProblem
	{
		#region Fields

		/// <summary>
		/// Contains type of <see cref="ITask"/> implementation used in the current problem.
		/// </summary>
		private readonly Type typeOfTask;

		/// <summary>
		/// Contains type of <see cref="ITaskResult"/> implementation used in the current problem.
		/// </summary>
		private readonly Type typeOfResult;

		/// <summary>
		/// Contains unique name of the current problem.
		/// </summary>
		private readonly string name;

		/// <summary>
		/// Contains description text of the current problem.
		/// </summary>
		private readonly string descriptionText;

		/// <summary>
		/// Contains raw bytecode of assembly that contains implementation of the current problem.
		/// </summary>
		private readonly byte[] rawAssembly;

		/// <summary>
		/// Contains description of this problem.
		/// </summary>
		private readonly ProblemDescription description;

		/// <summary>
		/// Contains implementation aspects of this problem.
		/// </summary>
		private readonly ProblemImplementation implementation;

		#endregion

		#region Initialization

		/// <summary>
		/// Initializes a new instance of the <see cref="ProblemBase"/> class.
		/// </summary>
		/// <param name="name">Unique problem name.</param>
		/// <param name="descriptionText">Problem description text.</param>
		/// <param name="typeOfTask">Type of <see cref="ITask"/> implementation used in the problem.</param>
		/// <param name="typeOfResult">Type of <see cref="ITaskResult"/> implementation used in the problem.</param>
		protected ProblemBase(string name, string descriptionText, Type typeOfTask, Type typeOfResult)
		{
			if (String.IsNullOrEmpty(name))
				throw new ArgumentNullException("name");
			if (String.IsNullOrEmpty(descriptionText))
				throw new ArgumentNullException("descriptionText");
			if (typeOfTask == null)
				throw new ArgumentNullException("typeOfTask");
			if (typeOfResult == null)
				throw new ArgumentNullException("typeOfResult");
			//// TODO: implement runtime check for typeOfTask
			if (typeOfResult.GetGenericTypeDefinition() != typeof(TaskResult<>))
				throw new ArgumentException("Type of result should be TaskResult<>.", "typeOfResult");

			this.name = name;
			this.descriptionText = descriptionText;
			this.typeOfTask = typeOfTask;
			this.typeOfResult = typeOfResult;

			string assemblyPath = this.typeOfTask.Assembly.Location;
			if (assemblyPath == null)
				throw new InvalidOperationException(string.Format(
					CultureInfo.InvariantCulture, "Can't get location for assembly '{0}'", this.typeOfTask.Assembly.FullName));
			this.rawAssembly = File.ReadAllBytes(assemblyPath);

			List<string> serializerKnownTypes = new List<string>
                                                    {
                                                        this.typeOfTask.AssemblyQualifiedName,
                                                        this.typeOfResult.AssemblyQualifiedName
                                                    };
			this.description = new ProblemDescription(this.descriptionText);
			this.implementation = new ProblemImplementation(serializerKnownTypes, this.rawAssembly);
		}

		#endregion

		#region Events

		/// <summary>
		/// Raises immediately after task of the current problem has been solved.
		/// </summary>
		public abstract event EventHandler<TaskSolvedEventArgs> TaskSolved;

		/// <summary>
		/// Raises immediately after current problem problem has been solved.
		/// </summary>
		public abstract event EventHandler<EventArgs> ProblemSolved;

		#endregion

		#region Properties

		/// <summary>
		/// Gets type of <see cref="ITask"/> implementation used in the current problem.
		/// </summary>
		public Type TypeOfTask
		{
			get { return this.typeOfTask; }
		}

		/// <summary>
		/// Gets type of <see cref="ITaskResult"/> implementation used in the current problem.
		/// </summary>
		public Type TypeOfResult
		{
			get { return this.typeOfResult; }
		}		

		/// <summary>
		/// Gets unique name of the current problem.
		/// </summary>
		public string Name
		{
			get { return this.name; }
		}

		/// <summary>
		/// Gets unique name of the current problem.
		/// </summary>
		public string DescriptionText
		{
			get { return this.descriptionText; }
		}

		/// <summary>
		/// Gets description of this problem.
		/// </summary>
		public ProblemDescription Description
		{
			get { return this.description; }
		}

		/// <summary>
		/// Gets implementation aspects of this problem.
		/// </summary>
		public ProblemImplementation Implementation
		{
			get { return this.implementation; }
		}

		/// <summary>
		/// Gets a value indicating whether current problem was solved (all the tasks were solved).
		/// </summary>
		public abstract bool IsCompleted { get; }

		/// <summary>
		/// Gets total count of the tasks of the current problem.
		/// </summary>
		public abstract int TaskCount { get; }

		/// <summary>
		/// Gets total count of the completed tasks of the current problem.
		/// </summary>
		public abstract int CompletedTaskCount { get; }

		#endregion

		#region Methods

		/// <summary>
		/// Retrieves task that is pending for execution.
		/// </summary>
		/// <param name="userName">Name of the user who wants to take a task.</param>
		/// <returns>Pending task.</returns>
		public abstract ITask GetPendingTask(string userName);

		/// <summary>
		/// Submits result of solved task.
		/// </summary>
		/// <param name="result">Task result.</param>
		/// <param name="userName">Name of the user who wants to submit result.</param>
		public abstract void SubmitResult(ITaskResult result, string userName);

		/// <summary>
		/// Gets if problem has tasks that were not solved yet.
		/// </summary>
		/// <returns>True, if problem has some tasks to solve; otherwise, false.</returns>
		public abstract bool GetIfHasTasksToSolve();

        #endregion
	}
}
